package resources;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
//import java.util.Random;

import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.Timer;

import org.jfree.data.xy.XYDataItem;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import componentEnhancement.CDataProperties;
import componentEnhancement.DataHandler;
import componentEnhancement.Scaler;

import cii_functions.IDataCommuniactor;
import cii_lib.CCII_ACCESS_INTERF;
import cii_lib.CIIdata;
import cii_lib.CiiLibException;

import testy.CiiDataInterface;
import testy.TestHardware;




public class DataGenerator extends Timer implements ActionListener {
	
	private static final Logger log = LoggerFactory.getLogger(DataGenerator.class);
	private static final long serialVersionUID = 7550761539440566048L;
		private XYSeriesCollection	dataset;
		private double				x=0;
		private double	start = 0, step = 1;
		private CCII_ACCESS_INTERF ciiint;
		private Object dataHandle = null;
		private CDataProperties properties = new CDataProperties();
		private String name = null;
		private long operationID = 0;
		/**
		 * Field determinating interface reading type.<Br>
		 * 0 - chart reading. Adres is incrementing along read actions<br>
		 * 1 - standalone reading. Read simple interface with specifed interval.
		 */
		private int interfaceType =0;

		/**
		 * Constructor.
		 *
		 * @param interval the interval (in milliseconds)
		 */
		public DataGenerator(int interv, Object handler, CCII_ACCESS_INTERF intrf, String name) {
			super(interv, null);
			dataHandle = handler;
			ciiint = intrf;
			setName(name);
			log.debug("DATA handlers: "+DataRelays.addDataReader(getName(),this));
			addActionListener(this);
			if (handler instanceof XYSeriesCollection) {
				x = start;
				this.dataset = (XYSeriesCollection)handler;
				properties.setProperty("Simple","0");
			} else 
			interfaceType=1;
		}
		
		DataGenerator(int interval, XYSeriesCollection dataset, CCII_ACCESS_INTERF intre) {
			super(interval, null);
			ciiint=intre;
			DataRelays.addDataReader(getName(),this);
			interfaceType=0;
			addActionListener(this);
			x = start;
			this.dataset = dataset;
			properties.setProperty("Simple","0");
		}
		
		/**
		 * 
		 */
		public DataGenerator(int interv, Object handler, CCII_ACCESS_INTERF intrf) {
			super(interv, null);
			dataHandle = handler;
			ciiint = intrf;
			log.debug("DATA handlers: "+DataRelays.addDataReader(getName(),this));
			interfaceType=1;
			addActionListener(this);
		}
		
		public DataGenerator(DataHandler dh) {
			super(600,null);
			DataRelays.addDataReader(getName(),this);
		}
		
		public DataGenerator(int interv, String[] handler, CCII_ACCESS_INTERF intrf) {
			super(interv, null);
			DataRelays.addDataReader(getName(),this);
			interfaceType=1;
			addActionListener(this);
//			try {
//				Class c = Class.forName(handler[0]);
//				Method mthl[] = c.getDeclaredMethods();
//				for (int m=0;m<mthl.length;m++) {
//					log.debug("met : " + mthl[m].getName());
//				}
//			} catch (ClassNotFoundException e) {
//				// TODO Auto-generated catch block
//				log.error("", e);
//			}
			
			ciiint = intrf;
		}

		public void setStepPos(double arg) {
			step=arg;
		}
		
		public void setStartPos(double arg) {
			start = arg;
			x = start;
		}
		
		public XYSeriesCollection getCollection() {
			return dataset;
		}
		
		/**
		 * Adds a new free/total memory reading to the interface.
		 *
		 * @param event the action event.
		 */
		public void actionPerformed(ActionEvent event) {
			if (interfaceType==0) actionPerformedChart(event);
			if (interfaceType==1) actionPerformedSimple(event);
			operationID++;
		}
		
		/**
		 * Adds a new free/total memory reading for single interface.
		 *
		 * @param event the action event.
		 */
		public void actionPerformedSimple(ActionEvent event) {
			CIIdata wynik = ciiint.Read(0, 0, new CIIdata(ciiint.Width()));
			if (dataHandle instanceof CIIdata) {
				dataHandle = wynik;
			}
			if (dataHandle instanceof JTextField) {
				((JTextField) dataHandle).setText(""+wynik.GetValue());
			}
			if (dataHandle instanceof JTextPane) {
				JTextPane logPane1= (JTextPane)dataHandle;
				logPane1.selectAll();
				logPane1.setCaretPosition(logPane1.getSelectionEnd());
				logPane1.setEditable(true);
				logPane1.replaceSelection(""+wynik.GetValue()+"\n");
				logPane1.setEditable(false);
			}
			if (dataHandle instanceof IDataCommuniactor) {
				((IDataCommuniactor) dataHandle).writeData(wynik,getName());
			}
		}
		
		
		/**
		 * Adds a new free/total memory reading to the chart dataset.
		 *
		 * @param event the action event.
		 */
		public void actionPerformedChart(ActionEvent event) {
			log.debug(event.getActionCommand());
			// Points for dirty area bounding rectangle.
			List<Point2D> ptsBounds = new ArrayList<Point2D>();
			// Series.
			List<XYSeries> seriesList = dataset.getSeries();

			//Scaler
			Scaler scal = new Scaler();
			// Insert last points.
			for (XYSeries series : seriesList) {
				if (!series.isEmpty()) {
					ptsBounds.add(new Point2D.Double(series.getX(series.getItemCount() - 1).doubleValue(), series.getY(
						series.getItemCount() - 1).doubleValue()));
				}
			}
//			XYSeries xs = new XYSeries(new Comparable<Long> () {
//				
//				@Override
//				public int compareTo(Long o) {
//					// TODO Auto-generated method stub
//					return 0;
//				}
//			});
//			dataset.addSeries(xs);
			for (int seriesIndex = 0; seriesIndex < seriesList.size(); ++seriesIndex) {
				XYSeries series = seriesList.get(seriesIndex);
				Point2D.Double pt = new Point2D.Double();
//				series.add(pt.x=x,pt.y= Math.sin((x-(2*Math.random()))/20)*50+50);
				CIIdata data = new CIIdata(ciiint.GetItem().Width);
					int tempIncType = TestHardware.incrementType;
					TestHardware.incrementType=4;
					ciiint.Read((int)x, 0, data);  //FIXME exception: adress overload
					TestHardware.incrementType=tempIncType;
					if (!(x>(ciiint.Number()*ciiint.Repeat()))) {
						if (properties.contains("Normalize")){
						scal.setReferenceValue(new Long(properties.get("Normalize")));
						pt.y= scal.parseValue(data).GetValue();//+(10*Math.random());
						} else
						pt.y = data.GetValue();
						log.debug("val::" +data.GetBin0());
						if (properties.contains("Segment")) {
							String get = properties.get("Segment");
							int sta = new Integer(get.split(":")[0]);
							int sto = new Integer(get.split(":")[1]);
							String va = data.GetBin0();
							//najmlodszy bit po prawej
							String tmBit = va.substring(va.length()-sto, va.length()-sta);
							BigInteger aa = new BigInteger(tmBit,2);
							log.debug("segment z datagen: start="+sta+" sto="+sto + " rep="+tmBit +" war="+ aa);
							
							
							pt.y = aa.doubleValue();
						}
					series.add(pt.x=x,pt.y);
					} else this.stop();					
				ptsBounds.add(pt);
			}
			if (!ptsBounds.isEmpty()) {
				Point2D firstPt = ptsBounds.get(0);
				Rectangle2D bounds = new Rectangle2D.Double(firstPt.getX(), firstPt.getY(), 0, 0);
				for (int i = 1; i < ptsBounds.size(); ++i) {
					Point2D pt = ptsBounds.get(i);
					bounds.add(pt);
				}
				// Dataset event with the dirty rectangle.
//				dataset.fireDatasetChanged(bounds);
				
			}
			x+=step;
		}
		/**
		 * Starts data generator with defined interval and action command for registered listeners.		 * 
		 * @param actCmd Command name
		 */
		public void start(String actCmd ) {
			this.setActionCommand(actCmd);
			operationID = 0;
			this.start();
		}
		
		/* (non-Javadoc)
		 * @see javax.swing.Timer#stop()
		 */
		@Override
		public void stop() {
			log.debug(getName()+" stoping id: "+ operationID);
			super.stop();
		}

		public CCII_ACCESS_INTERF getInterface() {
			return ciiint;
		}
		
		public void setInterface(CCII_ACCESS_INTERF in) {
			ciiint = in;
		}
		
		public CDataProperties getProperties() {
			return properties;
		}

		public void setProperties(CDataProperties properties) {
			this.properties = properties;
		}

		public String getName() {
			if (name!=null) {
				return name;
			}
			else {
				return ciiint.Name();
			}
		}
		
		public long getCorrelationId () {
			return operationID;
		}

		public void setName(String name) {
			this.name = name;
		}
}